package classfile

/*
方法表：
java程序方法体里面的代码经过javac编译器处理之后，最终变为字节码指令，存储在Code属性内
但是需要注意：抽象类和接口不存在Code属性
如果把Java程序中的信息分为代码和元数据，那么在整个Class文件中Code属性用于描述代码，所有其他的数据项目都用于描述元数据
*/
type CodeAttribute struct {
	cp ConstantPool
	// The value of the max_stack item gives the maximum depth of the operand stack of this method
	maxStack uint16 // 操作数栈的最大深度
	// The value of the max_locals item gives the number of local variables in the local variable array allocated upon invocation of this method
	maxLocals uint16 // 局部方法表的最大深度
	// code_length 这个字段直接就不进行添加了，如果后续使用直接 len(code)即可
	// 存储Java源程序编译后生成的字节码指令
	code            []byte
	exceptionTable  []*ExceptionTableEntry
	attributesCount uint16
	attributes      []AttributeInfo
}

func (self *CodeAttribute) readInfo(reader *ClassReader) {
	self.maxStack = reader.readUint16()
	self.maxLocals = reader.readUint16()
	codeLength := reader.readUint32()
	self.code = reader.readBytes(codeLength)
	self.exceptionTable = readExceptionTable(reader)
	self.attributesCount = readAttCount(reader)
	self.attributes = readAttributes(reader, self.cp, self.attributesCount)
}

func (self *CodeAttribute) MaxStack() uint {
	return uint(self.maxStack)
}
func (self *CodeAttribute) MaxLocals() uint {
	return uint(self.maxLocals)
}
func (self *CodeAttribute) Code() []byte {
	return self.code
}
func (self *CodeAttribute) ExceptionTable() []*ExceptionTableEntry {
	return self.exceptionTable
}

type ExceptionTableEntry struct {
	startPc   uint16
	endPc     uint16
	handlerPc uint16
	catchType uint16
}

func readExceptionTable(reader *ClassReader) []*ExceptionTableEntry {
	exceptionTableLength := reader.readUint16()
	exceptionTable := make([]*ExceptionTableEntry, exceptionTableLength)
	for i := range exceptionTable {
		exceptionTable[i] = &ExceptionTableEntry{
			startPc:   reader.readUint16(),
			endPc:     reader.readUint16(),
			handlerPc: reader.readUint16(),
			catchType: reader.readUint16(),
		}
	}
	return exceptionTable
}

func (self *ExceptionTableEntry) StartPc() uint16 {
	return self.startPc
}
func (self *ExceptionTableEntry) EndPc() uint16 {
	return self.endPc
}
func (self *ExceptionTableEntry) HandlerPc() uint16 {
	return self.handlerPc
}
func (self *ExceptionTableEntry) CatchType() uint16 {
	return self.catchType
}
